package com.webssky.jcseg.core;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.util.Properties;

import com.webssky.jcseg.ASegment;

/**
 * Dictionary Factory to create singleton Dictionary
 * object. a path of the class that has implemented the IDictionary
 * interface must be given first. <br />
 * 
 * @author	chenxin <br />
 * @email	chenxin619315@gmail.com <br />
 * {@link http://www.webssky.com}
 */
public class DictionaryFactory {
	
	/**
	 * lexicon property file 
	 */
	public static final String LEX_PROPERTY_FILE = "jcseg.properties";
	
	/**
	 * Dictionary singleton quote
	 */
	private static ADictionary __instance = null;
	
	/**
	 * lexicon Property object 
	 */
	private static Properties lexPro;
	
	/**
	 * return the IDictionary instance
	 * 
	 * @param 	__dicClass
	 * @return	IDictionary
	 */
	public static ADictionary createDictionary(String __dicClass) {
		if ( __instance == null )
			__instance = loadLexicon(__dicClass);
		return __instance;
	}
	
	private DictionaryFactory() {}
	
	/**
	 * set the IDicontary instance.
	 * 
	 * @param dic
	 */
	public static void setDictionary( ADictionary dic ) {
		__instance = dic;
	}
	
	/**
	 * load the lexcicon
	 * 
	 * @param __dicClass::the path of the class that has implemeted
	 * 			the IDictionary interface.
	 * @return IDictionary
	 */
	private static ADictionary loadLexicon(String __dicClass) {
		ADictionary dic = null;
		try {
			Class<?> _class = Class.forName(__dicClass);
			Constructor<?> cons = _class.getConstructor();
			dic = ( ADictionary ) cons.newInstance();
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("can't load the ADictionary extends class " +
					"with path ["+__dicClass+"]");
			System.exit(1);
		}
		
		try {
			File[] files = getLexiconFiles();
			for ( int j = 0; j < files.length; j++ ) {
				loadWordsFromFile(dic, files[j], "UTF-8");
			}
			
		} catch (LexiconException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return dic;
	}
	
	/**
	 * get all the lexicon file.
	 * 		this will parse the the lexicon.property file first. 
	 * 
	 * @throws LexiconException 
	 * @throws IOException 
	 */
	private static File[] getLexiconFiles() throws LexiconException, IOException {
		lexPro = new Properties();
		File[] files = null;
		
		/*
		 * load the mapping from the property file.
		 * 1.load the the jcseg.properties located with the jar file.
		 * 2.load the jcseg.propertiess from the current directory.
		 */
System.out.println(ASegment.JAR_HOME+File.separator+LEX_PROPERTY_FILE);
		//File pro_file = new File(ASegment.JAR_HOME+"/"+LEX_PROPERTY_FILE);
		File pro_file = new File(ASegment.JAR_HOME+File.separator+LEX_PROPERTY_FILE);
		if ( pro_file.exists() ) 
			lexPro.load(new FileReader(pro_file));
		else {
			//InputStream is = DictionaryFactory.class.getResourceAsStream("/"+LEX_PROPERTY_FILE);
			InputStream is = DictionaryFactory.class.getResourceAsStream(File.separator+LEX_PROPERTY_FILE);
			if ( is != null ) lexPro.load(new BufferedInputStream( is )); 
		}
		
		//the lexicon path
		String lexPath = lexPro.getProperty("lexicon.path");
System.out.println("lexPath:" + lexPath);		
		
		
		if ( lexPath == null )
			throw new LexiconException("Invalid lexicon.path value in lexicon.property file.");
		if ( lexPath.equals("jar.dir") )
			lexPath = ASegment.JAR_HOME;
		if ( lexPro.getProperty("lexicon.dir") != null )
			//lexPath = lexPath + "/" + lexPro.getProperty("lexicon.dir");
			lexPath = lexPath + File.separator + lexPro.getProperty("lexicon.dir");
		
		//the lexicon file prefix and suffix
		String suffix = "lex";
		String prefix = "lex";
		if ( lexPro.getProperty("lexicon.suffix") != null )
			suffix = lexPro.getProperty("lexicon.suffix");
		if ( lexPro.getProperty("lexicon.prefix") != null )
			prefix = lexPro.getProperty("lexicon.prefix");
		
		File path = new File(lexPath);
		if ( path.exists() == false )
			throw new LexiconException("lexicon path ["+lexPath+"] does'n exists.");
		
		/*
		 * load all the lexicon file
		 * under the lexicon path,
		 * that start with __prefix and end with __suffix.
		 */
		final String __suffix = suffix;
		final String __prefix = prefix;
		files = path.listFiles(new FilenameFilter(){
			@Override
			public boolean accept(File dir, String name) {
				return ( name.startsWith(__prefix)
							&& name.endsWith(__suffix));
			}
		});
		
		if ( lexPro.getProperty("jcseg.maxlen") != null )
			Config.MAX_LENGTH = Integer.parseInt(lexPro.getProperty("jcseg.maxlen"));
		if ( lexPro.getProperty("jcseg.mixcnlen") != null )
			Config.MIX_CN_LENGTH = Integer.parseInt(lexPro.getProperty("jcseg.mixcnlen"));
		if ( lexPro.getProperty("jcseg.icnname") != null
				&& lexPro.getProperty("jcseg.icnname").equals("1"))
			Config.I_CN_NAME = true;
		if ( lexPro.getProperty("jcseg.cnmaxlnadron") != null )
			Config.MAX_CN_LNADRON = Integer.parseInt(lexPro.getProperty("jcseg.cnmaxlnadron"));
		if ( lexPro.getProperty("jcseg.nsthreshold") != null )
			Config.NAME_SINGLE_THRESHOLD = Integer.parseInt(lexPro.getProperty("jcseg.nsthreshold"));
		if ( lexPro.getProperty("jcseg.pptmaxlen") != null ) 
			Config.PPT_MAX_LENGTH = Integer.parseInt(lexPro.getProperty("jcseg.pptmaxlen"));
		if ( lexPro.getProperty("jcseg.loadpinyin") != null
				&& lexPro.getProperty("jcseg.loadpinyin").equals("1")) 
			Config.LOAD_CJK_PINYIN = true;
		if ( lexPro.getProperty("jcseg.loadsyn") != null
				&& lexPro.getProperty("jcseg.loadsyn").equals("1") )
			Config.LOAD_CJK_SYN = true;
		if ( lexPro.getProperty("jcseg.loadpos") != null
				&& lexPro.getProperty("jcseg.loadpos").equals("1")) 
			Config.LOAD_CJK_POS = true;
		if ( lexPro.getProperty("jcseg.clearstopword") != null
				&& lexPro.getProperty("jcseg.clearstopword").equals("1"))
			Config.CLEAR_STOPWORD = true;
		
		lexPro = null;
		
		return files;
	}
	
	/**
	 * load all the words in the lexicon file,
	 * 		into the dictionary.
	 * 
	 * @param ADictionary lex
	 * @param File file 
	 */
	private static void loadWordsFromFile( ADictionary dic, File file, String charset ) {
		
		InputStreamReader ir = null;
		BufferedReader br = null;
		
		try {
			ir = new InputStreamReader(
					new FileInputStream( file ), charset);
			br = new BufferedReader(ir);
			
			String line = null;
			boolean isFirstLine = true;
			int t = -1;
			while ( (line = br.readLine()) != null ) {
				line = line.trim();
				if ( "".equals(line) ) continue;
				//swept the notes
				if ( line.indexOf('#') == 0 ) continue;
				//the first line fo the lexicon file.
				if ( isFirstLine == true ) {
					t = ADictionary.getIndex(line);
					//System.out.println(line+", "+t);
					isFirstLine = false;
					if ( t >= 0 ) continue;
				}
				
				//special lexicon
				if ( line.indexOf('/') == -1 ) {
					if ( line.length() <= Config.MAX_LENGTH ) {
						dic.add(t, line, IWord.T_CJK_WORD);
					}
				} 
				//normal words lexicon file
				else {
					String[] wd = line.split("/");
					if ( wd.length > 4 ) 
						dic.add(t, wd[0], Integer.parseInt(wd[4]), IWord.T_CJK_WORD);
					else
						dic.add(t, wd[0], IWord.T_CJK_WORD);
					
					IWord w = dic.get(t, wd[0]);
					//System.out.println(wd.length);
					//set the pinying of the word.
					if ( Config.LOAD_CJK_PINYIN && ! "null".equals(wd[2]) ) {
						w.setPinyin(wd[2]);
					}
					//set the syn words.
					if ( Config.LOAD_CJK_SYN && ! "null".equals(wd[3]) ) {
						String[] syns = wd[3].split(",");
						for ( int j = 0; j < syns.length; j++ ) 
							w.addSyn(syns[j].trim());
					}
					//set the word's part of speech
					if ( Config.LOAD_CJK_POS && ! "null".equals(wd[1]) ) {
						String[] pos = wd[1].split(",");
						for ( int j = 0; j < pos.length; j++ ) 
							w.addPartSpeech(pos[j].trim());
					}
				}
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if ( ir != null ) ir.close();
				if ( br != null ) br.close();
			} catch (IOException e) {}
		}
		
	}
	
	/**
	 * configurate the lexicon property file.
	 * 		the property file is under the path of,
	 * 		System.getProperty("user.dir");
	 * 
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static void setLexiconCfg() throws FileNotFoundException, IOException {
		File file = new File(System.getProperty("user.dir")+"/"+LEX_PROPERTY_FILE);
		Properties pro = new Properties();
		pro.load(new FileReader(file));
		pro.setProperty("lexicon.path", "jar.dir");
		pro.setProperty("lexicon.dir", "lexicon");
		pro.setProperty("lexicon.suffix", "lex");
		pro.setProperty("lexicon.prefix", "lex");
		pro.setProperty("jcseg.maxlen", "4");
		pro.setProperty("jcseg.mixcnlen", "2");
		pro.setProperty("jcseg.icnname", "1");
		pro.setProperty("jcseg.cnmaxlnadron", "1");
		pro.setProperty("jcseg.nsthreshold", "1000000");
		pro.setProperty("jcseg.pptmaxlen", "15");
		pro.setProperty("jcseg.loadpinyin", "0");
		pro.setProperty("jcseg.loadsyn", "1");
		pro.setProperty("jcseg.loadpos", "0");
		
		pro.store(new FileWriter(file), "jcseg properties file.\n" +
				"                 \n" +
				"Attention: do not change the key name.\n" +
				"and better not change the lexicon.path, when it's value is jar.dir we will check\n" +
				"	path of the jcseg jar file.\n" +
				"if any error happen, LexiconException will be throws out..\n" +
				"  icnname default value is 0 - close the chinese name identify, 1 to start it.\n"+
				"@author chenxin\n" +
				"@link http://www.webssky.com\n");
	}
/*	
	public static void main(String args[]) throws FileNotFoundException, IOException {
		setLexiconCfg();
		//ADictionary dic = DictionaryFactory.createDictionary("com.webssky.jcseg.Dictionary");
		//System.out.println("size: "+dic.size(ILexicon.CN_LNAME_ADORN));
		//System.out.println("match(ILexicon.CN_LNAME_ADORN, 老): "+dic.match(ILexicon.CN_LNAME_ADORN, "老"));
	}*/
}